package chap5

import scala.beans.BeanProperty

object Chap5 {
  // 1. Improve the Counter class in Section 5.1, "Simple Classes and Parameterless
  // Methods," on page 51 so that it doesn't turn negative at Int.MaxValue.
  class Counter(var step: Int) {
    private var value = 0
    def increment() { if (value < Int.MaxValue) value += step }
    def current = value
  }

  def counterTest() {
    val c = new Counter(10000);
    val max = Int.MaxValue.toLong
    println(max)
    var ci: Long = 0L
    while (ci < max) {
      c.increment()
      println(c.current)
      ci = c.current.toLong + c.step
      if (ci > max) {
        c.step = 1
        ci = c.current.toLong + c.step
      }
    }
    //execute 3 times increment
    c.increment()
    c.increment()
    c.increment()
    println(max)
    println(c.current)
    if (Int.MaxValue == c.current) println("ok") else println("somewhere wrong")
  }

  // 2. Write a class BankAccount with methods deposit and withdraw, and a read-only
  // property balance.
  class BankAccount(var _balance: Double) {
    def balance = _balance
    def deposit(money: Double) = _balance += money

    def withdraw(money: Double) = if (money < _balance) _balance -= money
  }

  def bankAccountTest() {
    val bc = new BankAccount(150.5)
    bc.deposit(15)
    println(bc.balance)
    bc.withdraw(15.5)
    println(bc.balance)
  }
  // 3. Write a class Time with read-only properties hours and minutes and a 
  //    method before(other: Time): Boolean that checks whether this time comes
  //    before the other. A Time object should be constructed as new 
  //    Time(hrs, min), where hrs is in military time format (between 0 and 23).
  class Time(val hours: Int = 0, val minutes: Int = 0) {
    def before(other: Time): Boolean = {
      if (hours < other.hours || hours == other.hours && minutes < other.minutes) true
      else false
    }
  }

  def timeTest() {
    val t1 = new Time(10, 26)
    val t2 = new Time(15, 30)
    println(t1.before(t2) == true)

    val t3 = new Time2(10, 26)
    val t4 = new Time2(15, 30)
    println(t3.before(t4) == true)
  }

  // 4. Reimplement the Time class from the preceding exercise so that the 
  //    internal representation is the number of minutes since midnight (between
  //    0 and 24 × 60 – 1). Do not change the public interface. That is, client
  //    code should be unaffected by your change.
  class Time2(val hours: Int = 0, val minutes: Int = 0) {

    private val mins = hours * 60 + minutes

    def before(other: Time2): Boolean = {
      if (mins < other.mins) true
      else false
    }
  }

  // 5. Make a class Student with read-write JavaBeans properties name (of type String)
  // and id (of type Long). What methods are generated? (Use javap to check.) Can
  // you call the JavaBeans getters and setters in Scala? Should you?
  class Student(@BeanProperty var name: String, @BeanProperty var id: Long) {}
  //What methods are generated? (Use javap to check.)
  /*  
   *  F:\scala_space\lessonScala\bin\chap5>javap Chap5$Student.class
   *  Compiled from "Chap5.scala"
   *  public class chap5.Chap5$Student {
   *  public java.lang.String name();
   *  public void name_$eq(java.lang.String);
  public void setName(java.lang.String);
  public long id();
  public void id_$eq(long);
  public void setId(long);
  public java.lang.String getName();
  public long getId();
  public chap5.Chap5$Student(java.lang.String, long);
}*/

  def studentTest() {
    val stu = new Student("John", 60)
    println(stu.getName())
  }

  // 6. In the Person class of Section 5.1, "Simple Classes and Parameterless Methods,"
  // on page 51, provide a primary constructor that turns negative ages to 0.
  class Person(var age: Int = 0) {
    if (age < 0) age = 0

    def test() {
      println(age)
    }
  }

  // 7. Write a class Person with a primary constructor that accepts a string containing
  // a first name, a space, and a last name, such as new Person("Fred Smith"). Supply
  // read-only properties firstName and lastName. Should the primary constructor
  // parameter be a var, a val, or a plain parameter? Why?
  // => It should be a plain parameter as it's not used in any of the methods
  class Person2(name: String) {
    private var fal = name.split(" ")
    private val firstName = fal(0)
    private val lastName = fal(1)

    def test() {
      println(firstName + " " + lastName)
    }
  }

  //8. Make a class Car with read-only properties for manufacturer, model name,
  //and model year, and a read-write property for the license plate. Supply four
  //constructors. All require the manufacturer and model name. Optionally,
  //model year and license plate can also be specified in the constructor. If not,
  //the model year is set to -1 and the license plate to the empty string. Which
  //constructor are you choosing as the primary constructor? Why?
  class Car(val manufacturer: String, val modelName: String, val modelYear: Int = -1, val licensePlate: String = "") {

    def this(manufacturer: String, modelName: String) {
      this(manufacturer, modelName, -1, "")
    }

    def this(manufacturer: String, modelName: String, modelYear: Int) = {
      this(manufacturer, modelName, modelYear, "")
    }

    def this(manufacturer: String, modelName: String, licensePlate: String) {
      this(manufacturer, modelName, -1, licensePlate)
    }

    override def toString() = {
      "[" + manufacturer + ", " + modelName + ", " + modelYear + ", '" + licensePlate + "']"
    }

  }

  class Employee() {
    private var _name = "John"
    private var _salary = 0.0
    def this(name: String, salary: Double) = {
      this()
      _name = name
      _salary = salary
    }
  }
  def main(args: Array[String]): Unit = {

    // counterTest()
    // bankAccountTest()
    // timeTest()
    //studentTest()

    //new Person(-5).test()
    //    new Person2("Fred Smith").test()
    println(new Car("dongfanghong", "sun", "Spring").toString())
  }
}